﻿using System;
using System.Collections.Generic;
using WiMo.Games.Inputs;
using WiMo.Games;
using WiMo.Games.Drawables;
using WiMo.Games.Audio;
using WiMo.Games.Menus;

namespace BalloonCaptain
{
    public class Game : IGame
    {
        private const int MNU_RESTART = 100;
        private const int MNU_EXIT = 101;
        private int _shipsRemaining;
        private IMenuComponent _mainMenu;

        private const int INT_MAX_LANDERS = 1;

        List<ISpriteImage> _landers;
        ISpriteImage _lander;
        ISpriteImage _background;
        ISpriteImage _splashScreen;
        ISpriteImage _shipsRemainingLander;
        IGameAudio _landingAudio;
        IGameAudio _crashAudio;

        int _landerIndex;
        int _maxLanders = 3;
        int _score;

        ISpriteShape _landingZone;
        ISpriteShape _ground;

        public enum GameStates
        {
            StartUp,
            Playing,
            Crashed,
            ShowingExplosion,
            GameOver
        }

        public GameStates gameState;

        public Game(IGameEngine engine)
        {
            Engine = engine;
            Engine.ContentManager.ContentLocation = "Content";
            Engine.EnableDiagnostics = true;
            Engine.DiagnosticsFontColor = Color.White;
            Engine.EndVibrate();
            
            _landers = new List<ISpriteImage>();
            _landerIndex = 0;

            gameState = GameStates.StartUp;
        }

        public void Initialize()
        {
            Engine.PhysicsManager.Gravity = new Vector2(0, 10f);
            Engine.InputManager.UseAccelerometerAsThumbPad = true;
        }

        public void BeginRun()
        {

        }

        public void CreateMenus()
        {
            _mainMenu = Engine.MenuManager.Create("Main Menu");
            _mainMenu.MenuSelected += new EventHandler<SelectedMenuEventArgs>(_mainMenu_MenuSelected);
            _mainMenu.AddMenu("Restart", MNU_RESTART);
            _mainMenu.AddMenu("Exit", MNU_EXIT);

            Engine.InputManager.RightSoftButton = "Exit";
            Engine.InputManager.UseAccelerometerAsThumbPad = true;

            Engine.RegisterComponent(_mainMenu);
        }

        void _mainMenu_MenuSelected(object sender, SelectedMenuEventArgs e)
        {
            switch (e.SelectedMenu.MenuId)
            {
                case MNU_RESTART:
                    Reset();
                    gameState = GameStates.Playing;
                    Engine.Resume();
                    _nextLanderStart = (int)_lastGameTime.TotalGameTime.TotalSeconds + 3;
                    _lander = null;
                    _mainMenu.Hide();

                    break;
                case MNU_EXIT:
                    Engine.Exit();
                    break;
            }
        }

        #region LoadContent
        public void LoadContent()
        {
            _landingAudio = Engine.AudioManager.LoadSound("Landing");
            _crashAudio = Engine.AudioManager.LoadSound("Bang");

            _ground = Engine.AssetFactory.CreatePolygon();
            _ground.CollisionShape = CollisionShapes.LineList;
            _ground.Position = new PointF(0, 260);
            _ground.AddPoint(new Location(0, 20));
            _ground.AddPoint(new Location(10, 25));
            _ground.AddPoint(new Location(30, 40));
            _ground.AddPoint(new Location(60, 50));
            _ground.AddPoint(new Location(100, 30));
            _ground.AddPoint(new Location(120, 25));
            _ground.AddPoint(new Location(150, 4));
            _ground.AddPoint(new Location(160, 3));
            _ground.AddPoint(new Location(175, 5));
            _ground.AddPoint(new Location(185, 8));
            _ground.AddPoint(new Location(200, 10));
            _ground.AddPoint(new Location(220, 15));
            _ground.AddPoint(new Location(240, 5));
            _ground.AddPoint(new Location(240, 60));
            _ground.AddPoint(new Location(0, 60));
            _ground.AddPoint(new Location(0, 0));
            _ground.CalcBoundingRect();

            _ground.CollisionCategory = CollisionCategory.Two;
            _ground.Collision += _ground_Collision;
            _ground.SetTexture(Engine.ContentManager, "Ground");
            _ground.BorderWidth = 2;

            _landingZone = Engine.AssetFactory.CreateRectangle();

            _landingZone.BorderColor = Color.Red;
            _landingZone.FillColor = Color.Red;
            _landingZone.Position = new PointF(100, 290);
            _landingZone.Size = new WiMo.Games.Size(5, 2);
            _landingZone.Collision += _landingZone_Collision;
            _landingZone.CollisionCategory = CollisionCategory.One;
            _landingZone.CollisionShape = CollisionShapes.Rectangle;

            for (var idx = 0; idx < INT_MAX_LANDERS; ++idx )
            {
                var lander = Engine.ContentManager.LoadImage("Lander2");
                
                lander.AddImage(Engine.ContentManager, "Lander");
                lander.AddImage(Engine.ContentManager, "Explosion");
                lander.Position = new PointF(120, 50);
                lander.Mass = 1.0f;
                lander.CoefRestitution = 0.9f;
                lander.IsStatic = false; 
                lander.AffectedByGravity = true;
                lander.ColorKey = Color.White;
                lander.CanCollidesWithList.Add(CollisionCategory.One);
                lander.CanCollidesWithList.Add(CollisionCategory.Two);
                lander.IsVisible = false;
                lander.IsStatic = false;

                Engine.PhysicsManager.Add(lander);
                Engine.CollisionManager.Add(lander);
                _landers.Add(lander);
            }

            _splashScreen = Engine.ContentManager.LoadImage("SplashScreen");
            _splashScreen.Position = new PointF(0, 0);
            _splashScreen.Origin = Origin.TopLeft;

            _background = Engine.ContentManager.LoadImage("Background");
            _background.Position = new PointF(0, 0);
            _background.Origin = Origin.TopLeft;

            _shipsRemainingLander = Engine.ContentManager.LoadImage("Lander");
            _shipsRemainingLander.Position = new PointF(170, 5);
            _shipsRemainingLander.ColorKey = Color.White;
            _shipsRemainingLander.Origin = Origin.TopLeft;

            _lander = _landers[0];

            Engine.CollisionManager.Add(_ground);
            Engine.CollisionManager.Add(_landingZone);

            Engine.Pause();
        }
        #endregion

        void StartLander(int landerIndex)
        {
            var lander = _landers[landerIndex];

            lander.Position = new PointF(Engine.Randomizer.Next(200) + 20, 50);
            lander.Force = Vector2.Zero;
            lander.IsVisible = true;
            lander.IsStatic = false;

            if (_lander == null)
            {
                _lander = lander;
                _landerIndex = landerIndex;
                _lander.ImageIndex = 1;
            }
        }

        void StartNextLander()
        {
            for(var idx = 0; idx < _landers.Count; ++idx)
            {
                if(!_landers[idx].IsVisible)
                {
                    StartLander(idx);
                    return;
                }
            }
        }

        void Reset()
        {
            _score = 0;
            _shipsRemaining = 5;
           
            foreach(var lander in _landers)
            {
                lander.IsStatic = true;
                lander.IsVisible = false;
            }
        }

        void _landingZone_Collision(object sender, CollisionEventArgs e)
        {

            _landingAudio.Play();

            _score += 20;
            e.Sprite.IsStatic = true;
            e.Sprite.IsVisible = false;
        }

        void _ground_Collision(object sender, CollisionEventArgs e)
        {
            
            if (e.CollisionType == CollisionEventArgs.CollisionTypes.Sprite)
            {
                _crashAudio.Play(); 
                e.Sprite.IsStatic = true;
                _lander.ImageIndex = 2;
                _lander.Move(new Vector2(0, 10));

                if (_shipsRemaining == 0)
                    gameState = GameStates.GameOver;
                else
                    _shipsRemaining--;

                Engine.Vibrate(50);

                var crashedLanderIndex = _landerIndex;
                new System.Threading.Thread(() =>
                {
                    System.Threading.Thread.Sleep(1500);
                    _landers[crashedLanderIndex].IsVisible = false;
                    _landers[crashedLanderIndex].ImageIndex = 1;
                }
                ).Start();
            }
        }

        IInputManager _previous;

        private void SelectNextLander()
        {
            if (_lander != null)
                _lander.ImageIndex = 0;

            for (var idx = _landerIndex + 1; idx < _landers.Count; ++idx )
            {
                if(_landers[idx].IsVisible)
                {
                    _landerIndex = idx;
                    _lander = _landers[idx];
                    _lander.ImageIndex = 1;
                    return;
                }
            }

            for(var idx = 0; idx < _landerIndex; ++idx)
            {
                if (_landers[idx].IsVisible)
                {
                    _landerIndex = idx;
                    _lander = _landers[idx];
                    _lander.ImageIndex = 1;
                    return;
                }                
            }

            _lander = _landers[_landerIndex];
            _lander.ImageIndex = 1;
        }

        public void ShowMenu()
        {
            Engine.Pause();
            _mainMenu.Show();
        }

        private int _nextLanderStart = 0;
        GameTime _lastGameTime;
        public void Update(GameTime gameTime)
        {
            _lastGameTime = gameTime;

            switch (gameState)
            {
                case GameStates.GameOver:
                case GameStates.StartUp:
                    if (Engine.InputManager.WasScreenTouched(_previous))
                    {
                        Reset();
                        gameState = GameStates.Playing;
                        Engine.Resume();
                        _nextLanderStart = (int)gameTime.TotalGameTime.TotalSeconds + 3;
                        _lander = null;
                    }
                    break;

                case GameStates.Playing:
                    if (Engine.InputManager.WasPressed(_previous, Keys.F1))
                    {
                        Engine.Pause();
                        ShowMenu();
                    }

                    if (gameTime.TotalGameTime.TotalSeconds > _nextLanderStart)
                    {
                        _nextLanderStart = (int)gameTime.TotalGameTime.TotalSeconds + (Engine.Randomizer.Next(2) + 2);
                        StartNextLander();
                    }

                    if (Engine.InputManager.WasPressed(_previous, Keys.Return))
                    {
                        SelectNextLander();
                        Engine.Vibrate(10);
                    }

                    if(_lander != null)
                    {
                        if (Engine.InputManager.IsPressed(_previous, Keys.Up, 50))
                            _lander.ApplyForce(new Vector2(0, -2));

                        if (Engine.InputManager.IsPressed(_previous, Keys.Right, 500))
                            _lander.ApplyForce(new Vector2(5, 0));

                        if (Engine.InputManager.IsPressed(_previous, Keys.Left, 500))
                            _lander.ApplyForce(new Vector2(-5, 0));
                    }

                    break;                
            }


            if (Engine.InputManager.WasPressed(_previous, Keys.RightSoftButton))
                Engine.Exit();


            _previous = this.Engine.InputManager.GetKeysState();
        }

        public void Draw(GameTime gameTime)
        {
            switch(gameState)
            {
                case GameStates.Playing:
                    Engine.Display.Add(_background);
                    Engine.Display.Add(_ground);

                    foreach(var lander in _landers)
                        Engine.Display.Add(lander);
                    
                    Engine.Display.Add(_landingZone);
                    
                    Engine.Display.Add(_shipsRemainingLander);
                    Engine.Display.DrawText(new PointF(201, 6), "x" + _shipsRemaining, Engine.DefaultFont, Color.Black);
                    Engine.Display.DrawText(new PointF(200, 5), "x" + _shipsRemaining, Engine.DefaultFont, Color.White);
                    
                    if(_lander != null)
                    {
                        int force = -(int)(_lander.Velocity.Y * 2);
                        force = Math.Max(0, force);
                        force = Math.Min(force, 100);

                        Engine.Display.FillRectangle(new Rectangle(2, 20, 100, 4), Color.White, Color.Black, 1, Origin.TopLeft);

                        if (force < 16)
                            Engine.Display.FillRectangle(new Rectangle(2, 20, force, 4), Color.Green, Color.Black, 1, Origin.TopLeft);
                        else
                            Engine.Display.FillRectangle(new Rectangle(2, 20, force, 4), Color.Red, Color.Black, 1, Origin.TopLeft);                        
                    }

                    Engine.Display.DrawText(new PointF(3, 3), string.Format("Score {0}", _score), Engine.DefaultFont, Color.Black);
                    Engine.Display.DrawText(new PointF(2, 2), string.Format("Score {0}", _score), Engine.DefaultFont, Color.White);                    
                break;

                case GameStates.StartUp:
                    Engine.Display.Add(_splashScreen);
                    Engine.Display.DrawText(new PointF(61, 301), "Touch To Begin", Engine.DefaultFont, Color.Black);
                    Engine.Display.DrawText(new PointF(60, 300), "Touch To Begin", Engine.DefaultFont, Color.White);                    
                break;

                case GameStates.GameOver:
                    Engine.Display.Add(_splashScreen);
                    Engine.Display.DrawText(new PointF(81, 11), "Game Over", Engine.DefaultFont, Color.Black);
                    Engine.Display.DrawText(new PointF(80, 10), "Game Over", Engine.DefaultFont, Color.White);
                    Engine.Display.DrawText(new PointF(81, 251), string.Format("Score {0}", _score), Engine.DefaultFont, Color.Black);
                    Engine.Display.DrawText(new PointF(80, 250), string.Format("Score {0}", _score), Engine.DefaultFont, Color.White);
                    Engine.Display.DrawText(new PointF(21, 301), "Touch the screen to Play Again", Engine.DefaultFont, Color.Black);
                    Engine.Display.DrawText(new PointF(20, 300), "Touch the screen to Play Again", Engine.DefaultFont, Color.White);
                    
                    Engine.Pause();
                break;
            }
        }

        public bool CanExit()
        {
            return true;
        }

        public IGameEngine Engine { get; set; }
    }
}
